<?php
// +----------------------------------------------------------------------
// | 概率算法 ( Probability )
// | 基本思想：
// | 典型实例：
// |   1）抽奖区间
// +----------------------------------------------------------------------

namespace helper\algorithm;

class Probability
{
    public function demo()
    {
        $arr = array(
            array('id' => 1, 'name' => '特等奖', 'v' => 1),
            array('id' => 2, 'name' => '一等奖', 'v' => 5),
            array('id' => 3, 'name' => '二等奖', 'v' => 10),
            array('id' => 4, 'name' => '三等奖', 'v' => 12),
            array('id' => 5, 'name' => '四等奖', 'v' => 22),
            array('id' => 6, 'name' => '没中奖', 'v' => 50)
        );

        var_dump($this->get_rand($arr));
    }

    /**
     * 抽奖区间算法
     *
     * -------------------------------------------------------------
     *
     * 不同概率的抽奖原理就是把0到*（比重总数）的区间分块
     * 分块的依据是物品占整个的比重，再根据随机数种子来产生0-* 中的某个数
     * 判断这个数是落在哪个区间上，区间对应的就是抽到的那个物品。
     * 随机数理论上是概率均等的，那么相应的区间所含数的多少就体现了抽奖物品概率的不同。
     */

    /**
     * get_rand
     *
     * @param $proArr
     * @return array
     */
    function get_rand($proArr)
    {
        $result = array();
        $arr = [];
        foreach ($proArr as $key => $val) {
            $arr[$key] = $val['v'];
        }
        $proSum = array_sum($arr);      // 计算总权重
        $randNum = mt_rand(1, $proSum);
        $d1 = 0;
        $d2 = 0;
        for ($i = 0; $i < count($arr); $i++) {
            $d2 += $arr[$i];
            if ($i == 0) {
                $d1 = 0;
            } else {
                $d1 += $arr[$i - 1];
            }
            if ($randNum >= $d1 && $randNum <= $d2) {
                $result = $proArr[$i];
            }
        }
        unset ($arr);
        return $result;
    }

    /**
     * 使用较多的为这个方法
     *
     * @param array $proArr 权重数组
     * @return array
     */
    function get_rands(array $proArr)
    {
        $result = array();
        $arr = [];
        foreach ($proArr as $key => $val) {
            $arr[$key] = $val['v'];
        }
        // 概率数组的总概率
        $proSum = array_sum($arr);
        asort($arr);
        // 概率数组循环
        foreach ($arr as $k => $v) {
            $randNum = mt_rand(1, $proSum);
            if ($randNum <= $v) {
                $result = $proArr[$k];//如果随机数小于等于当前权重,结束循环,数据放入响应数据内
                break;
            } else {
                $proSum -= $v;// 权重总和减去当前权重
            }
        }
        return $result;
    }
}